﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using BLToolkit.Data.Linq;

using JetBrains.Annotations;

using Rsdn.Janus.DataModel;
using Rsdn.SmartApp;

namespace Rsdn.Janus
{
	public static class DataHelper
	{
		public static IQueryable<T> GetTable<T>(this IDataContext context, Expression<Func<T, bool>> predicate)
			where T : class
		{
		    Table<T> table = context.GetTable<T>();

		    IQueryable<T> queryable;

		    queryable = table.Where(predicate);
		    return queryable;
		}

	    /// <summary>
		/// Получить перечислитель заданной колонки таблицы.
		/// </summary>
		/// <typeparam name="T">тип к которому привести значение колонки</typeparam>
		/// <param name="table">таблица</param>
		/// <param name="name">имя колонки для получения</param>
		/// <returns>перечислитель колонки таблицы</returns>
		public static IEnumerable<T> GetTableColumn<T>(this DataTable table, string name)
		{
			return
				table
					.Rows
					.Cast<DataRow>()
					.Select(row => (T)row[name]);
		}

		public static IJanusDataContext CreateDBContext([NotNull] this IServiceProvider provider)
		{
			if (provider == null)
				throw new ArgumentNullException("provider");

			return
				provider
					.GetRequiredService<IJanusDatabaseManager>()
					.CreateDBContext();
		}

		public static IEnumerable<IEnumerable<T>> SplitForInClause<T>(
			[NotNull] this IEnumerable<T> source,
			[NotNull]IServiceProvider provider)
		{
			if (source == null) throw new ArgumentNullException("source");
			if (provider == null) throw new ArgumentNullException("provider");
			return source.SplitToSeries(provider.MaxInClauseElements());
		}

		public static Table<IVariable> Vars([NotNull] this IDataContext db)
		{
			if (db == null) throw new ArgumentNullException("db");
			return db.GetTable<IVariable>();
		}

		public static IQueryable<IVariable> Vars(
			[NotNull] this IDataContext db,
			[CanBeNull] Expression<Func<IVariable, bool>> predicate)
		{
			return GetTable(db, predicate);
		}

		public static IDBVarsManager DBVars([NotNull] this IServiceProvider provider)
		{
			if (provider == null) throw new ArgumentNullException("provider");
			return provider.GetRequiredService<IDBVarsManager>();
		}

		public static int MaxInClauseElements([NotNull] this IServiceProvider provider)
		{
			if (provider == null) throw new ArgumentNullException("provider");
			return
				provider
					.GetRequiredService<IJanusDatabaseManager>()
					.GetCurrentDriver()
					.Formatter
					.MaxInClauseElements;
		}

        /// <exception cref="System.ArgumentNullException">source or selector is null.</exception>
        public static List<MsgBase> LoadMessages(IServiceProvider provider, IQueryable<IForumMessage> forumMessages, Table<IForumMessage> msgTable)
        {
            #region forum messages SQL
            //-- SET @p1 = 95947

            //SELECT
            //    [m].[mid] as [ID],
            //    [m].[tid] as [TopicID],
            //    [m].[pid] as [ParentID],
            //    [m].[gid] as [ForumID],
            //    [m].[Subject],
            //    [m].[dte] as [Date1],
            //    [m].[uid] as [UserID],
            //    [m].[uclass] as [UserClass],
            //    [m].[UserNick],
            //    [m].[Name],
            //    [m].[article_id] as [ArticleId],
            //    [m].[LastModerated],
            //    [m].[Closed],
            //    [m].[Message],
            //    [m].[IsMarked],
            //    [m].[IsRead],
            //    [m].[ReadReplies]
            //FROM
            //    [messages] [m]
            //        LEFT JOIN [messages] [t1] ON [m].[pid] = [t1].[mid]
            //WHERE
            //    [m].[pid] <> 0 AND [t1].[uid] = @p1
            //ORDER BY
            //    [m].[dte] DESC
            #endregion

            // breakpoint inside "new" is not possible
            IQueryable<LinearTreeMsg> msgs =
                forumMessages.Select(
                //(Expression<Func<IForumMessage, LinearTreeMsg>>)
                m =>
                new LinearTreeMsg(provider)
                {
                    ID = m.ID,
                    ForumID = m.ForumID,
                    TopicIDInternal = m.TopicID,
                    ParentID = m.ParentID,
                    Date = m.Date,
                    UserID = m.UserID,
                    UserNick = m.UserNick,
                    UserClass = (short)m.UserClass,
                    Subject = m.Subject,
                    IsRead = m.IsRead,
                    Rating = m.Rating(),
                    Smiles = m.SmileCount(),
                    Agrees = m.AgreeCount(),
                    Disagrees = m.DisagreeCount(),
                    RepliesCount = msgTable.Count(im => im.ParentID == m.ID),
                    //RepliesUnread = msgTable.Count(im => im.ParentID == m.ID),
                    //MsgCount = m.TopicInfos.Sum(ti => ti.AnswersCount),
                    //Unread = msgTable.Count(im => im.ID == m.ID && !m.IsRead),
                    //MeUnread = m.TopicInfos.Sum(ti => ti.AnswersToMeUnread)

                }
                );

            #region SQL

            //-- SET @p1 = 95947

            //SELECT
            //    [m].[mid] as [ID],
            //    [m].[gid] as [ForumID],
            //    [m].[tid] as [TopicID],
            //    [m].[pid] as [ParentID],
            //    [m].[dte] as [Date1],
            //    [m].[uid] as [UserID],
            //    [m].[UserNick],
            //    [m].[uclass] as [UserClass],
            //    [m].[Subject],
            //    [m].[IsRead],
            //    (
            //        SELECT
            //            Sum(Cast([r].[rate] as SmallInt) * [r].[rby])
            //        FROM
            //            [rating] [r]
            //        WHERE
            //            [m].[mid] = [r].[mid] AND [r].[rate] > 0
            //    ) as [c2],
            //    (
            //        SELECT
            //            Count(*)
            //        FROM
            //            [rating] [r1]
            //        WHERE
            //            [m].[mid] = [r1].[mid] AND [r1].[rate] = -2
            //    ) as [c3],
            //    (
            //        SELECT (Expression<Func<IForumMessage, int>>)(m => m.Rates.Count(r => r.RateType == MessageRates.Smile));
            //            Count(*)
            //        FROM
            //            [rating] [r2]
            //        WHERE
            //            [m].[mid] = [r2].[mid] AND [r2].[rate] = -4
            //    ) as [c4],
            //    (
            //        SELECT
            //            Count(*)
            //        FROM
            //            [rating] [r3]
            //        WHERE
            //            [m].[mid] = [r3].[mid] AND [r3].[rate] = 0
            //    ) as [c5],
            //    (
            //        SELECT
            //            Count(*)
            //        FROM
            //            [messages] [im]
            //        WHERE
            //            [im].[pid] = [m].[mid]
            //    ) as [c6]
            //FROM
            //    [messages] [m]
            //        LEFT JOIN [messages] [t1] ON [m].[pid] = [t1].[mid]
            //WHERE
            //    [m].[pid] <> 0 AND [t1].[uid] = @p1
            //ORDER BY
            //    [m].[dte] DESC

            #endregion

            IQueryable<MsgBase> queryable = msgs.Cast<MsgBase>();
            //IQueryable<MsgBase> result = queryable.DefaultIfEmpty();

            //List<MsgBase> messages;
            //if (result.Count() != 0)
            //{
            //    messages = result.ToList();
            //}
            //else
            //{
            //    messages = new List<MsgBase>();
            //}
            //return messages;
            List<MsgBase> messages;
            try
            {
                messages = queryable.ToList();
            }
            catch (TargetInvocationException ex)
            {
                //ignore this exception because it come with empty list
                messages = new List<MsgBase>();
            }
            return messages;
        }

	}
}